#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <pthread.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <signal.h>


#include "global.h"
#include "constantes.h"
#include "listas.h"
#include "sockets.h"
#include "configuracion.h"
#include "NIPC.h"
#include "RAID.h"

extern ListConfig * listaConfig; 
/*extern chs glChs;*/

int numeroSincro;
int contador;

int main ()
{	
   
    InfoIPC rInfoIPC;
    struct sockaddr_in Str_sockaddr_in_paraAcept,sin;
    int iAddress_size= sizeof(struct sockaddr_in);
    fd_set set_socketsHiloPrincipal;			/**lista de sockets que se van a hacer que se active el select del hilo principal cuando reciban algo**/		
    int iSocketEscuchaPedidosFSS,iSocket_temporal,iMayorSocket;	
	int *pSocketAux,*pSocketArrayParaConsola;	
    pthread_t ID_HiloParaConsolaEsperarConexionDiscos;
    int ret,i;
	cliente *pNodoCliente,*pAuxCliente;
	char * sContador=(char*)malloc(20);
	char *sSocket=(char *)malloc(20);
	
	numeroSincro=0;
	
    /*****INICIALIZACION DE VARIABLES******/
    Estado= FUERA_DE_SERVICIO;
    SubEstado = INCOMPLETO;
    ListaDiscos = NULL;
    ListaDiscos_Ult = NULL;
	ListaClientes = NULL;
    ListaClientes_Ult = NULL;	
	colaGlobal=NULL;
	colaGlobal_Ult=NULL;
	colaControl=NULL;
	colaControl_Ult=NULL;
    /************************************/
	
	listaConfig = NULL;
	cargarConfig();
	
	system("clear");
	
	printf("\nCargando Archivo de Configuracion...\n\n");
    printf("puertoFSS %s \n",obtenerValor("puertoFSS"));
    printf("puertoConex %s \n",obtenerValor("puertoConex"));
    
   
	pSocketArrayParaConsola =malloc(sizeof(int));
	pSocketAux = malloc(sizeof(int));						
	CrearYConectarDosSockets(pSocketArrayParaConsola,pSocketAux,obtenerValor("ipLocal"));
	ret=pthread_create(&ID_HiloParaConsolaEsperarConexionDiscos,NULL,(void *)ConsolaEsperaNuevosDiscos ,(void *)pSocketAux);    
	recibirIPC(*pSocketArrayParaConsola, &rInfoIPC); /*RAID pasa a estar operacional*/
	
	pthread_mutex_init(&mutex,NULL); /*mutex para la cola global*/
	pthread_mutex_init(&mutex2,NULL);/*mutex para la cola de control de escritura*/
	pthread_mutex_init(&mutex3,NULL);/*mutex para el contador que indica numero de pedido*/
	
	iSocketEscuchaPedidosFSS=PrepararSocket (atoi(obtenerValor("puertoFSS")),obtenerValor("ipLocal"));   
	escuchar(iSocketEscuchaPedidosFSS,1000000);
    
	contador=0;	
	
	while(1)
	{			
		FD_ZERO(&set_socketsHiloPrincipal);
		FD_SET(iSocketEscuchaPedidosFSS, &set_socketsHiloPrincipal);
		iMayorSocket = iSocketEscuchaPedidosFSS;
	
		pAuxCliente = ListaClientes;
		while(pAuxCliente != NULL)
		{
			FD_SET(pAuxCliente->socketCliente , &set_socketsHiloPrincipal);	
			if(pAuxCliente->socketCliente > iMayorSocket)
				iMayorSocket = pAuxCliente->socketCliente;
			pAuxCliente = pAuxCliente->Sgt;
		}
	
		
		select(iMayorSocket + 1,&set_socketsHiloPrincipal, NULL, NULL, NULL);
       		
		
		/*ACEPTO CONEXIONES DE CLIENTES FSS*/
		if( FD_ISSET(iSocketEscuchaPedidosFSS, &set_socketsHiloPrincipal ) > 0 )
		{
			if((iSocket_temporal = accept (iSocketEscuchaPedidosFSS,(struct sockaddr *)&sin,&iAddress_size ))== -1)
				return ERRORACCEPT;	
			

			recibirIPC(iSocket_temporal,&rInfoIPC);
		
		
		
			if (iCantidadDeDAConectados == 0) {
                printf("\n\nConexion rechazada, no hay discos conectados \n\n");
				rInfoIPC.payLoadLength=5;
				generarStructIPC("nada",TERMINARCONEXION,&rInfoIPC);
				enviarIPC(iSocket_temporal,&rInfoIPC);
				recibirIPC(iSocket_temporal,&rInfoIPC);
				cerrarSocket(iSocket_temporal);
				continue;
			}			
			
			
			pNodoCliente = (cliente *)malloc(sizeof(cliente));
			pNodoCliente->socketCliente=iSocket_temporal;
			
			agregarAListaDeClientes(&pNodoCliente);
			/*imprimirCola(ListaClientes);*/
			
			rInfoIPC.payLoadLength=5;
			generarStructIPC("nada",HSOK,&rInfoIPC);
			enviarIPC(pNodoCliente->socketCliente,&rInfoIPC);
			
		}	
		
		
		/*RECIBO PEDIDOS DE ALGUN SOCKET CLIENTE FSS*/
		pAuxCliente = ListaClientes;
		while(pAuxCliente != NULL)
		{
			if( FD_ISSET(pAuxCliente->socketCliente , &set_socketsHiloPrincipal ) > 0 ){
				break;
			}
			pAuxCliente = pAuxCliente->Sgt;
		}
		if(pAuxCliente != NULL)
		{	
			
			struct ListaIDdescriptors * NodoDescriptores = (struct ListaIDdescriptors *)malloc(sizeof(struct ListaIDdescriptors));	
			
			/*Se desconecta cliente*/
			if(recibirIPC(pAuxCliente->socketCliente,&rInfoIPC)<0){	
				close(pAuxCliente->socketCliente);
				FD_CLR(pAuxCliente->socketCliente, &set_socketsHiloPrincipal);
				SacarDeLista_Clientes(&pAuxCliente);
				printf("Se desconecto cliente FSS \n\n");
				free(pAuxCliente);
				continue;
			}
			
			
			/*Lleno el nodo de pedido*/
			intacad(pAuxCliente->socketCliente,sSocket);  
			strcpy(NodoDescriptores->sSocketPorElCualResponder,sSocket);	
			NodoDescriptores->numeroSector=atoi(rInfoIPC.payload);
			NodoDescriptores->owner=0;
			NodoDescriptores->Sgt = NULL;
			NodoDescriptores->Ant = NULL;
			
			contador++;			
			
			intacad(contador,sContador);
			strcpy(NodoDescriptores->sDescriptorID_delPedido,sContador);
			
			if((rInfoIPC.payLoadDescriptor == READ_SECTOR_REQUEST) || (rInfoIPC.payLoadDescriptor == READ_SECTOR_REQUEST_LAST))
			{
				NodoDescriptores->operacion=0;
				printf("Recibi pedido de lectura del sector %i \n", NodoDescriptores->numeroSector);
				
				/*Lo agrego a la cola global*/
				if(pthread_mutex_lock(&mutex)!=0){
					perror("pthread_mutex_lock");
					exit(EXIT_FAILURE);
				}
				
				AgregarAColaGlobal(&NodoDescriptores,&colaGlobal,&colaGlobal_Ult);
			
				if(pthread_mutex_unlock(&mutex)!=0){
					perror("pthread_mutex_unlock");
					exit(EXIT_FAILURE);
				}
			}
			
			if(rInfoIPC.payLoadDescriptor == WRITE_SECTOR_REQUEST)
			{
				/*Recibo la data*/
				NodoDescriptores->operacion=1;
				InfoIPC rInfoIPC3;
				recibirIPC(pAuxCliente->socketCliente,&rInfoIPC3);	
				memcpy(NodoDescriptores->data,rInfoIPC3.payload, 512);
				printf("Recibi pedido de escritura del sector %i con la sgte data: %s\n", NodoDescriptores->numeroSector,NodoDescriptores->data);
				
				
				/*Lo agrego a la cola global*/
				if(pthread_mutex_lock(&mutex)!=0){
					perror("pthread_mutex_lock");
					exit(EXIT_FAILURE);
				}
				
				AgregarAColaGlobal(&NodoDescriptores,&colaGlobal,&colaGlobal_Ult);
			
				if(pthread_mutex_unlock(&mutex)!=0){
					perror("pthread_mutex_unlock");
					exit(EXIT_FAILURE);
				}
				
				int contaux=1;
				int longit=longitud2(ListaDiscos);
				
				while(contaux!=longit){
				
					contaux++;
					
					pedido * NodoDescriptores2 = (pedido *)malloc(sizeof(struct ListaIDdescriptors));	
					
					memcpy(NodoDescriptores2,NodoDescriptores,sizeof(pedido));
					
					/*Lo agrego a la cola global*/
					if(pthread_mutex_lock(&mutex)!=0){
						perror("pthread_mutex_lock");
						exit(EXIT_FAILURE);
					}
					
					AgregarAColaGlobal(&NodoDescriptores2,&colaGlobal,&colaGlobal_Ult);
					/*imprimirCola2(colaGlobal);*/
				
					if(pthread_mutex_unlock(&mutex)!=0){
						perror("pthread_mutex_unlock");
						exit(EXIT_FAILURE);
					}
					
				}
				
				printf("sContador %s \n",sContador);
				
				/*Creo el nodo de control para la lista de referncias de escritura*/
				t_control* NodoControl = (t_control *)malloc(sizeof(t_control));			
				strcpy(NodoControl->sDescriptorID_delPedido,sContador);
				
				NodoControl->discosAEspejar=longit;
				
				
				NodoControl->Sgt = NULL;
				NodoControl->Ant = NULL;
					
				if(pthread_mutex_lock(&(mutex2))!=0){
					perror("pthread_mutex_lock");
					exit(EXIT_FAILURE);
				}	
				
				AgregarAColaControl(&NodoControl);
		
				if(pthread_mutex_unlock(&(mutex2))!=0){
					perror("pthread_mutex_unlock");
					exit(EXIT_FAILURE);
				}	
				
				free(rInfoIPC3.payload);
				
			}	
						
			asignarPedido(&NodoDescriptores,1);
		}
	}
	free(pSocketArrayParaConsola);
	free(pSocketAux);
	return 0;
}


int asignarPedido(struct ListaIDdescriptors ** nodoPedido,int cayo) {
   
	disco *pAuxDisco;
	int payload;
	char *sCont=(char *)malloc(20);
	char *sAux=(char *)malloc(20);
	char *buffer;
	disco *dsk;
	int contaux;
	
	if ((*nodoPedido)->operacion==0) {
			
		if(!cayo)
			dsk=(disco *)BuscarDiscoPorNombre("Disco1");/*Para prueba reallocate*/
		else
			dsk = (disco *)elegirDisco((*nodoPedido)->numeroSector);
		
		if(dsk==NULL){
			printf("No hay disco listo para atender esa solicitud \n");
			return -1;
		}	
			
		
		strcpy((*nodoPedido)->discoAsignado,dsk->sNombre);
		dsk->pendings++;
		memset(sAux,'\0',20);
		intacad((* nodoPedido)->numeroSector,sAux);	
		int tamanio_buff ;
		payload=READ_SECTOR_REQUEST;
		tamanio_buff = strlen((* nodoPedido)->sDescriptorID_delPedido) + 1 + strlen(sAux) + 2;
		buffer = (char *) malloc (sizeof(char) * tamanio_buff);
		strcpy(buffer, (* nodoPedido)->sDescriptorID_delPedido);
		strcat(buffer, ",");
		strcat(buffer, sAux);
		strcat(buffer, ";");
		InfoIPC rInfoIPC3;
		rInfoIPC3.payLoadLength=tamanio_buff;
		generarStructIPC(buffer,payload,&rInfoIPC3);
		enviarIPC(dsk->iSocketHDAParaDA,&rInfoIPC3);	
		(* nodoPedido)->estado = 1;
		printf("Disco elegido para realizar lectura del sector %i es: %s \n",(*nodoPedido)->numeroSector,dsk->sNombre);
		CambiaPosicionDelNodo(&dsk);
		
		
	} else {
		
		strcpy((*nodoPedido)->discoAsignado,"");
		pAuxDisco = ListaDiscos;
		

		pAuxDisco->pendings++;
		memset(sAux,'\0',20);
		intacad((* nodoPedido)->numeroSector,sAux);	
		int tamanio_buff ;
		payload=WRITE_SECTOR_REQUEST;
		tamanio_buff = strlen((* nodoPedido)->sDescriptorID_delPedido) + 1 + strlen(sAux) + 1 + 512 + 1;
		buffer = (char *) malloc (sizeof(char) * tamanio_buff);
		strcpy(buffer, (* nodoPedido)->sDescriptorID_delPedido);
		strcat(buffer, ",");
		strcat(buffer, sAux);
		strcat(buffer, ";");
		memcpy(buffer + strlen((* nodoPedido)->sDescriptorID_delPedido) + 1 + strlen(sAux) + 1, (* nodoPedido)->data, 512);
		InfoIPC rInfoIPC3;
		rInfoIPC3.payLoadLength=tamanio_buff;
		generarStructIPC(buffer,payload,&rInfoIPC3);
		enviarIPC(pAuxDisco->iSocketHDAParaDA,&rInfoIPC3);	
		(* nodoPedido)->estado = 1;
		printf("Se asigno escritura del sector %i al disco: %s \n",(*nodoPedido)->numeroSector,pAuxDisco->sNombre);
	
		pAuxDisco=pAuxDisco->Sgt;
		
		
		
		while(pAuxDisco != NULL){	
		
				
			pAuxDisco->pendings++;
			int tamanio_buff ;
			tamanio_buff = strlen((* nodoPedido)->sDescriptorID_delPedido) + 1 + strlen(sAux) + 1 + 512 + 1;
			buffer = (char *) malloc (sizeof(char) * tamanio_buff);
			strcpy(buffer, (* nodoPedido)->sDescriptorID_delPedido);
			strcat(buffer, ",");
			strcat(buffer, sAux);
			strcat(buffer, ";");
			memcpy(buffer + strlen((* nodoPedido)->sDescriptorID_delPedido) + 1 + strlen(sAux) + 1, (* nodoPedido)->data, 512);
			InfoIPC rInfoIPC3;
			rInfoIPC3.payLoadLength=tamanio_buff;
			generarStructIPC(buffer,payload,&rInfoIPC3);
			enviarIPC(pAuxDisco->iSocketHDAParaDA,&rInfoIPC3);	
			(* nodoPedido)->estado = 1;
			printf("Se asigno escritura del sector %i al disco: %s \n",(*nodoPedido)->numeroSector,pAuxDisco->sNombre);
			contaux++;
			pAuxDisco=pAuxDisco->Sgt;
			
		}	
	}
	free(buffer);
	free(sAux);
	return 0;
	
}

disco * elegirDisco(int numeroSector) {
	
	int count = 32767;
	disco *pAuxDisco;
	disco *pDiscoElegido;
	
	pAuxDisco=NULL;
	pDiscoElegido=NULL;
	
	pAuxDisco = ListaDiscos;
	
	while(pAuxDisco != NULL){
	
		
		if ((numeroSector < pAuxDisco->sectores) && (pAuxDisco->pendings < count)) {
			pDiscoElegido=pAuxDisco;
			count = pAuxDisco->pendings;
		} 
		
		pAuxDisco=pAuxDisco->Sgt;
		
	}
	
    return pDiscoElegido;
}	


